home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
news
/
readers
/
skim-0.8
/
skim-0
/
skim-0.8.4
/
NNTPStream.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-02-18
|
8KB
|
310 lines
/*
* NAME
* NNTPStream.c
* COPYRIGHT
* Skim - Off-line news reading package optimized for slow lines.
* Copyright (C) 1996 Rene W.J. Pijlman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* VERSION
* Skim version 0.8.4.
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <assert.h>
#include "VarBuf.h"
#include "Skim.h"
#include "NNTPStream.h"
#include "StandardIO.h"
FILE_ID("$Header: /home/rene/sys/CVS_MasterSourceRepository/skim/NNTPStream.c,v 1.5 1996/02/18 11:40:19 rene Exp $");
/*
* Return a socket with a TCP/IP connection to the specified service (port)
* on the specified host.
*/
static int TCP_ConnectionOpen( char * HostName, char * ServiceName )
{
int Status;
struct protoent * Protocol;
struct hostent * Host;
struct servent * Service;
struct sockaddr_in SocketAddress;
int Socket;
Protocol = getprotobyname( "tcp" );
if ( Protocol != NULL )
{
Host = gethostbyname( HostName );
if ( Host != NULL )
{
Service = getservbyname( (char *)ServiceName, "tcp" );
if ( Service != NULL )
{
Socket = socket( AF_INET, SOCK_STREAM, Protocol->p_proto );
if ( Socket != NotASocket )
{
memset( &SocketAddress, 0, sizeof SocketAddress );
SocketAddress.sin_family = AF_INET;
memmove( (char *)&SocketAddress.sin_addr,
Host->h_addr, Host->h_length );
SocketAddress.sin_port = Service->s_port;
Status = connect( Socket,
(struct sockaddr *)&SocketAddress,
sizeof( SocketAddress ) );
if ( Status == -1 )
{
SIOPrintError( "connect" );
SIOPrintf( StandardError,
"Does the NNTPSERVER environment variable point to your news server?\n"
"Note that not all servers allow access to all clients.\n" );
exit( EXIT_FAILURE );
}
}
else
{
SIOPrintError( "socket" );
exit( EXIT_FAILURE );
}
}
else
{
SIOPrintf( StandardError,
"Cannot translate service name '%s' to port number\n"
"Check /etc/protocols.\n",
ServiceName );
exit( EXIT_FAILURE );
}
}
else
{
herror( "gethostbyname" );
SIOPrintf( StandardError,
"Does the NNTPSERVER environment variable point to your news server?\n" );
exit( EXIT_FAILURE );
}
}
else
{
SIOPrintf( StandardError,
"Cannot translate protocol name 'tcp' to number\n"
"Check /etc/protocols.\n" );
exit( EXIT_FAILURE );
}
return Socket;
}
/* Return a bidirectional standard I/O stream to the NNTP news server. */
StandardIO NNTPStreamOpen( void )
{
int Socket;
char * NNTP_Server;
StandardIO NewsServer = SIOCreate();
VarBuf PseudoFileName = VBCreate();
char * p;
Boolean TalkingToINN = False;
if ( ( NNTP_Server = getenv( "NNTPSERVER" ) ) == NULL )
{
SIOPrintf( StandardError,
"Environment variable NNTPSERVER is not set\n" );
exit( EXIT_FAILURE );
}
/*
* The user can enable an INN-specific kludge by appending ":INN" to
* the hostname in NNTPSERVER.
*/
if ( ( p = strchr( NNTP_Server, ':' ) ) != NULL )
{
if ( !strcasecmp( p, ":INN" ) )
{
*p = '\0';
TalkingToINN = True;
}
}
Socket = TCP_ConnectionOpen( NNTP_Server, "nntp" );
VBPrintf( PseudoFileName, "<NNTP connection to %s>", NNTP_Server );
SIOFileOpenFileDescriptorVB( NewsServer, Socket, PseudoFileName,
OpenModeReadAndWriteDiscardOld );
CheckStatusResponse( NewsServer, NULL, "2", NULL, TERMINATE_ON_ERROR );
/*
* INN has a design bug: when a client connects to the NNTP port INN
* may or may not speak NNTP depending on various circumstances.
*
* On Linux, UNIX domain sockets don't work. We have to set the INN
* configuration option HAVE_UNIX_DOMAIN to DONT, therefore, and the local
* hostname to the hosts.nntp config file. This causes INN to not speak
* NNTP when the client is on the same IP-address as the server, unless the
* reader issues the non-NNTP command "mode reader". This is explained in
* the INN FAQ.
*
* This is a kludge of course, but I needed it to be able to test skim
* with INN on my local linux machine.
*/
if ( TalkingToINN )
{
SIOInternetCommand( NewsServer, "mode reader" );
CheckStatusResponse( NewsServer, NULL, "2", NULL, TERMINATE_ON_ERROR );
}
VBDestroy( PseudoFileName );
return NewsServer;
}
void NNTPStreamClose( StandardIO NewsServer )
{
if ( NewsServer != NULL && SIOIsOpenForWrite(NewsServer) )
{
SIOInternetCommand( NewsServer, "quit" );
SIOFileClose( NewsServer );
}
SIODestroy( NewsServer );
}
Boolean CheckStatusResponse(
StandardIO NewsServer,
const char * Group,
const char * MustStartWithStatusResponse,
const char * ExtraMessage, /* May be NULL. */
Boolean TerminateOnError )
{
Boolean OK = True;
VarBuf StatusResponse = VBCreate();
SIOFlushBuffers( NewsServer );
VBReadLine( StatusResponse, NewsServer, WITHOUT_NEWLINE );
if ( strncmp( VBAsString(StatusResponse), MustStartWithStatusResponse,
strlen(MustStartWithStatusResponse) ) )
{
OK = False;
if ( Group != NULL )
{
SIOPrintf( StandardError, "On group %s:\n", Group );
}
SIOPrintf( StandardError,
"Error from NNTP server:\n%V",
StatusResponse );
if ( ExtraMessage != NULL )
{
SIOPrintf( StandardError, "\n%s\n", ExtraMessage );
}
if ( TerminateOnError )
{
exit( EXIT_FAILURE );
}
}
VBDestroy( StatusResponse );
return OK;
}
/*
* Read a text response from the NNTP server, convert it from RFC-977 format
* to UNIX text file format, and write it to `ResponseBuffer' (if not NULL),
* and/or `ResponseFile' (if not NULL), and or pass it as a parameter to
* function Output (if not NULL). The conversions which are applied are:
*
* - CR-LF pairs are converted to LF.
* - Double periods on the first character of a line are collapsed to a
* single period.
* - The terminating line with only a period is removed.
* - The function Convert is called for every line, if Convert is not NULL.
*/
void GetTextResponse(
StandardIO NewsServer,
ConversionFunction Convert,
VarBuf ResponseBuffer,
StandardIO Response,
OutputFunction Output )
{
Boolean Finished = False;
VarBuf LineOfTextResponse = VBCreate();
while ( !Finished &&
VBReadLine( LineOfTextResponse, NewsServer, WITHOUT_NEWLINE ) )
{
if ( !strcmp( VBAsString(LineOfTextResponse), ".\r" ) )
{
Finished = True;
}
else if ( !strncmp( VBAsString(LineOfTextResponse), "..", 2 ) )
{
VBShiftLeft( LineOfTextResponse, 1 );
}
if ( !Finished )
{
if ( VBSize(LineOfTextResponse) >= 1 &&
*(VBAsString(LineOfTextResponse) +
VBSize(LineOfTextResponse) - 1) == '\r' )
{
VBTruncate(LineOfTextResponse, VBSize(LineOfTextResponse) - 1);
}
if ( Convert != NULL )
{
Convert( LineOfTextResponse );
}
if ( Response != NULL )
{
SIOPrintf( Response, "%V\n", LineOfTextResponse );
}
if ( ResponseBuffer != NULL )
{
VBPrintf( ResponseBuffer, "%V\n", LineOfTextResponse );
}
if ( Output != NULL )
{
Output( LineOfTextResponse );
}
}
VBReset( LineOfTextResponse );
}
VBDestroy(LineOfTextResponse);
}